<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js">var _constants = require(&quot;../../utils/constants&quot;);

var mathAbs = _constants.mathAbs;

var matrixUtil = require(&quot;../../utils/affine_matrix_util&quot;);

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError(&quot;Invalid attempt to destructure non-iterable instance&quot;); }

function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === &quot;[object Arguments]&quot;)) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i &amp;&amp; _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n &amp;&amp; _i[&quot;return&quot;] != null) _i[&quot;return&quot;](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(&quot;Cannot call a class as a function&quot;); } }

function _defineProperties(target, props) { for (var i = 0; i &lt; props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (&quot;value&quot; in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

<span id='qrenderer-graphic-transform-TransformMgr'>/**
</span> * @class qrenderer.graphic.transform.TransformMgr
 * 
 * Global transform manager. When user drag the transform control and begin dragging, this manager will handle the events
 * and transform parameters for the selected element.
 * 
 * 全局变换管理器。当用户选中元素，开始拖动变换控制器时，此管理器负责处理事件、重新计算选中元素上的各项参数。
 */
var TransformMgr =
/*#__PURE__*/
function () {
  function TransformMgr(dispatcher) {
    _classCallCheck(this, TransformMgr);

    this.dispatcher = dispatcher;
  }

  _createClass(TransformMgr, [{
    key: &quot;startListen&quot;,
    value: function startListen() {
      this.stopListen();
      this.dispatcher.on(&quot;mousedown&quot;, this.mouseDownHandler1, this);
      return this;
    }
  }, {
    key: &quot;stopListen&quot;,
    value: function stopListen() {
      this._restoreSelection(); //incase there was a selected element


      this.selectedEl = null;
      this.lastHoveredControl = null;
      this._x = 0; //cache x axis

      this._y = 0; //cache y axis

      this._center = [0, 0]; //cache center point of bounding rect

      this._position;
      this._scale;
      this._rotation;
      this._width;
      this._height;
      this._transform;
      this._cursor = &#39;default&#39;; //cache cursor type

      this._elDraggable = false; //cache original draggable flag of element

      this._hasControls = false; //whether this.el has controls
      //remove all event listeners

      this.dispatcher.off(&quot;mousedown&quot;, this.mouseDownHandler1, this);
      this.dispatcher.off(&quot;mousedown&quot;, this.mouseDownHandler2, this);
      this.dispatcher.off(&quot;mousemove&quot;, this.mouseMoveHandler1, this);
      this.dispatcher.off(&quot;pagemousemove&quot;, this.mouseMoveHandler2, this);
      this.dispatcher.off(&quot;pagemouseup&quot;, this.mouseUpHandler, this);
      return this;
    }
  }, {
    key: &quot;mouseDownHandler1&quot;,
    value: function mouseDownHandler1(e) {
      var el = e.target;

      if (el &amp;&amp; el.transformable) {
        //click on an element
        this._clickElement(el);
      } else {
        //no element is clicked
        this.startListen();
      }
    }
  }, {
    key: &quot;_clickElement&quot;,
    value: function _clickElement(el) {
      this._restoreSelection();

      this.selectedEl = el;
      this._cursor = el.cursor;
      this._elDraggable = el.draggable; //cache original draggable flag

      this._hasControls = el.hasTransformControls = true;
      el.dirty(); //remove mousedown listener first, then start listen to mousemove 
      //and the second mousedown event

      this.dispatcher.off(&quot;mousedown&quot;, this.mouseDownHandler1, this);
      this.dispatcher.on(&quot;mousemove&quot;, this.mouseMoveHandler1, this);
      this.dispatcher.on(&quot;mousedown&quot;, this.mouseDownHandler2, this);
    }
  }, {
    key: &quot;_restoreSelection&quot;,
    value: function _restoreSelection() {
      if (this.selectedEl) {
        //restore original draggable flag
        this.selectedEl.draggable = this._elDraggable;
        this.selectedEl.hasTransformControls = false;
        this.selectedEl.dirty();
      }
    }
  }, {
    key: &quot;mouseMoveHandler1&quot;,
    value: function mouseMoveHandler1(e) {
      var _this = this;

      if (!this.selectedEl) {
        return;
      }

      var qrX = e.event.qrX;
      var qrY = e.event.qrY;
      this.lastHoveredControl = null;
      this.selectedEl.transformControls.forEach(function (control, index) {
        if (control.isHover(qrX, qrY)) {
          _this.lastHoveredControl = control;

          _this.dispatcher.interceptor.setCursor(control.cursor);
        }
      });
    }
  }, {
    key: &quot;mouseDownHandler2&quot;,
    value: function mouseDownHandler2(e) {
      var target = e.target;

      if (this.lastHoveredControl) {
        //click on a transform control
        this.selectedEl.draggable = false;
        this._x = e.offsetX;
        this._y = e.offsetY;
        this.dispatcher.off(&quot;mousemove&quot;, this.mouseMoveHandler1, this); //lockdown current clicked control, do not look for hovered control

        this.dispatcher.on(&quot;pagemousemove&quot;, this.mouseMoveHandler2, this);
        this.dispatcher.on(&quot;pagemouseup&quot;, this.mouseUpHandler, this);
      } else if (target &amp;&amp; target.id &amp;&amp; target.id.indexOf(&quot;el-&quot;) != -1) {
        //click on an element, FIXME:better way to determine whether the target is an element?
        this._clickElement(target);
      } else {
        //click on anywhere else
        this._hasControls = false;
        this.startListen();
      }
    }
  }, {
    key: &quot;mouseMoveHandler2&quot;,
    value: function mouseMoveHandler2(e) {
      var mouseX = e.offsetX; //x position of mouse in global space

      var mouseY = e.offsetY; //y position of mouse in global space

      var name = this.lastHoveredControl.name;

      if (name === &#39;SPIN&#39;) {
        this.handleRotate(mouseX, mouseY);
      } else {
        this.handleScale(mouseX, mouseY);
      }
    }
  }, {
    key: &quot;handleRotate&quot;,
    value: function handleRotate(mouseX, mouseY) {
      var bps = this.getTransformedBoundingRect();

      var _matrixUtil$minusVect = matrixUtil.minusVector([mouseX, mouseY], this._center);

      var _matrixUtil$minusVect2 = _slicedToArray(_matrixUtil$minusVect, 2);

      mouseX = _matrixUtil$minusVect2[0];
      mouseY = _matrixUtil$minusVect2[1];
      var sinp = matrixUtil.sinx.apply(matrixUtil, [mouseX, mouseY]);
      var cosp = matrixUtil.cosx.apply(matrixUtil, [mouseX, mouseY]);
      var radian = Math.asin(Math.abs(sinp));

      if (sinp &gt;= 0) {
        if (cosp &lt; 0) {
          radian = Math.PI - radian;
        }

        if (this._scale[1] &gt; 0) {
          //flip in Y direction
          radian = radian + Math.PI;
        }

        radian = radian - Math.PI / 2;
      } else {
        radian = -radian;

        if (cosp &lt; 0) {
          radian = -(Math.PI + radian);
        }

        if (this._scale[1] &lt; 0) {
          //flip in Y direction
          radian = radian + Math.PI;
        }

        radian = radian + Math.PI / 2;
      }

      var position = bps[0];
      position = matrixUtil.rotateVector(position, -radian);
      position = matrixUtil.addVector(position, this._center);
      this.selectedEl.position = position;
      this.selectedEl.rotation = -radian;
      this.selectedEl.dirty();
    }
  }, {
    key: &quot;handleScale&quot;,
    value: function handleScale(mouseX, mouseY) {
      var bps = this.getTransformedBoundingRect();

      var _this$transformMouseP = this.transformMousePoint(mouseX, mouseY),
          _this$transformMouseP2 = _slicedToArray(_this$transformMouseP, 2),
          tmx = _this$transformMouseP2[0],
          tmy = _this$transformMouseP2[1];

      var newSx = mathAbs(tmx / (this._width / 2));
      var newSy = mathAbs(tmy / (this._height / 2));
      var name = this.lastHoveredControl.name;

      if (name.indexOf(&quot;T&quot;) != -1) {
        newSy = tmy &gt;= 0 ? -newSy : newSy;
      } else if (name.indexOf(&quot;B&quot;) != -1) {
        newSy = tmy &gt;= 0 ? newSy : -newSy;
      } else {
        newSy = this._scale[1];
      }

      if (name.indexOf(&quot;L&quot;) != -1) {
        newSx = tmx &gt;= 0 ? -newSx : newSx;
      } else if (name.indexOf(&quot;R&quot;) != -1) {
        newSx = tmx &gt;= 0 ? newSx : -newSx;
      } else {
        newSx = this._scale[0];
      }

      var position = bps[0];

      if (name.indexOf(&quot;R&quot;) != -1) {
        position[0] = -tmx;
      } else if (name.indexOf(&quot;L&quot;) != -1) {
        position[0] = tmx;
      }

      if (name.indexOf(&quot;B&quot;) != -1) {
        position[1] = -tmy;
      } else if (name.indexOf(&quot;T&quot;) != -1) {
        position[1] = tmy;
      }

      position = matrixUtil.rotateVector(position, this._rotation);
      position = matrixUtil.addVector(position, this._center);
      this.selectedEl.position = position;
      this.selectedEl.scale = [newSx, newSy];
      this.selectedEl.dirty();
    }
  }, {
    key: &quot;mouseUpHandler&quot;,
    value: function mouseUpHandler(e) {
      this.selectedEl.draggable = this._elDraggable;
      this.dispatcher.off(&quot;mousedown&quot;, this.mouseDownHandler1, this);
      this.dispatcher.off(&quot;pagemousemove&quot;, this.mouseMoveHandler2, this);
      this.dispatcher.off(&quot;pagemouseup&quot;, this.mouseUpHandler, this);
      this.dispatcher.on(&quot;mousemove&quot;, this.mouseMoveHandler1, this);
      this.dispatcher.on(&quot;mousedown&quot;, this.mouseDownHandler2, this);
    }
<span id='qrenderer-graphic-transform-TransformMgr-method-getTransformedBoundingRect'>    /**
</span>     * @private
     * @method getTransformedBoundingRect
     * Get transformed bouding rect of selected element, including four corner points, center point of original bounding rect, 
     * and rotate control point. The coordinates returned by this method are in global space, the coordinate is based on the 
     * center of bounding rect.
     * 
     * 
     * 获取变换之后的边界矩形坐标，包括：4个角落上的坐标点、中心坐标点、旋转控制器的坐标点。此方法返回的坐标位于全局空间中，计算的
     * 坐标原点在边界矩形的中心点上。
     */

  }, {
    key: &quot;getTransformedBoundingRect&quot;,
    value: function getTransformedBoundingRect() {
      this._position = this.selectedEl.position; //current position in global space

      this._scale = this.selectedEl.scale; //current scale in global space

      this._width = this.selectedEl.shape.width //original width without transforming
      || this.selectedEl.style.width;
      this._height = this.selectedEl.shape.height //original height without transforming
      || this.selectedEl.style.height;
      this._rotation = this.selectedEl.rotation; //current rotation in global space

      this._center = [this._width / 2, this._height / 2]; //original centerpoint in local space

      var m = matrixUtil.create();
      m = matrixUtil.scale(m, this._scale);
      m = matrixUtil.rotate(m, this._rotation);
      m = matrixUtil.translate(m, this._position);
      this._transform = m;
      this._center = matrixUtil.transformVector(this._center, this._transform); //center point in global space

      var p0 = [0, 0];
      var p1 = [this._width, 0];
      var p2 = [this._width, this._height];
      var p3 = [0, this._height];
      var p4 = [this._width / 2, -50]; // covert coordinate to global space

      p0 = matrixUtil.transformVector(p0, this._transform);
      p1 = matrixUtil.transformVector(p1, this._transform);
      p2 = matrixUtil.transformVector(p2, this._transform);
      p3 = matrixUtil.transformVector(p3, this._transform);
      p4 = matrixUtil.transformVector(p4, this._transform); // move origin to this._center point

      p0 = matrixUtil.minusVector(p0, this._center);
      p1 = matrixUtil.minusVector(p1, this._center);
      p2 = matrixUtil.minusVector(p2, this._center);
      p3 = matrixUtil.minusVector(p3, this._center);
      p4 = matrixUtil.minusVector(p4, this._center); // rotate with element&#39;s rotation

      p0 = matrixUtil.rotateVector(p0, -this._rotation);
      p1 = matrixUtil.rotateVector(p1, -this._rotation);
      p2 = matrixUtil.rotateVector(p2, -this._rotation);
      p3 = matrixUtil.rotateVector(p3, -this._rotation);
      p4 = matrixUtil.rotateVector(p4, -this._rotation);
      return [p0, p1, p2, p3, p4, this._center];
    }
<span id='qrenderer-graphic-transform-TransformMgr-method-transformMousePoint'>    /**
</span>     * @private
     * @method transformMousePoint
     * Transform the cursor origin to the center point of bounding rect, then rotate the same angel as the element does.
     * 
     * 
     * 把光标的原点变换到边界矩形的中心点，并与元素保持相同的旋转角。
     * 
     * @param {*} x 
     * @param {*} y 
     */

  }, {
    key: &quot;transformMousePoint&quot;,
    value: function transformMousePoint(x, y) {
      var _matrixUtil$minusVect3 = matrixUtil.minusVector([x, y], this._center);

      var _matrixUtil$minusVect4 = _slicedToArray(_matrixUtil$minusVect3, 2);

      x = _matrixUtil$minusVect4[0];
      y = _matrixUtil$minusVect4[1];

      var _matrixUtil$rotateVec = matrixUtil.rotateVector([x, y], -this._rotation);

      var _matrixUtil$rotateVec2 = _slicedToArray(_matrixUtil$rotateVec, 2);

      x = _matrixUtil$rotateVec2[0];
      y = _matrixUtil$rotateVec2[1];
      //为什么这里的旋转是反向的？
      return [x, y];
    }
  }]);

  return TransformMgr;
}();

module.exports = TransformMgr;</pre>
</body>
</html>
